#ifndef PHYSICALMEMORYMANAGER_HPP
#define PHYSICALMEMORYMANAGER_HPP

#include <std/cstddef>
#include <cpp/BitSet.hpp>
#include <cpp/math.hpp>
#include <kernel/arch/i386/memory/Byte.hpp>
#include <kernel/multiboot/MemoryMap.hpp>
#include <kernel/arch/i386/memory/paging/paging.hpp>

namespace paging
{
class PhysicalMemoryManager
{
	public:
		PhysicalMemoryManager(const multiboot::MemoryMap& memoryMap);
		void* allocateFrame();
		void allocateFrame(void* address);
		void freeFrame(void* address);

	private:
		void setFrame(void* address, bool free);
		void parseMemoryMap();
		void freeMemoryRegion(const multiboot::SimpleMemoryRegion& region);
		static constexpr uintptr_t alignUp(uintptr_t address);
		static constexpr uintptr_t alignDown(uintptr_t address);

		static constexpr size_t _numberOfFrames{1 << (addressSize - math::log2(paging::pageSize))};
		BitSet<_numberOfFrames> _freeFrames;
};

}
namespace paging
{
constexpr uintptr_t PhysicalMemoryManager::alignUp(uintptr_t address)
{
	return (address + paging::pageSize - 1) & ~(paging::pageSize - 1);
}

constexpr uintptr_t PhysicalMemoryManager::alignDown(uintptr_t address)
{
	return address & ~(paging::pageSize - 1);
}

}

#endif
